﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System.Collections.Generic;
using System.Text;
using ScrumTable.Common;
using ScrumTable.Common.Collections;

#endregion

namespace ScrumTable.DL.Data.TFS.Services.TeamFoundation
{
    /// <summary>
    /// Specifies a query builder instance for the TFS WorkItem Query Language.
    /// </summary>
    public class WiqlQueryBuilder
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private const string ConditionOrLink = " OR ";
        private const string ConditionAndLink = " AND ";
        private const string IdCondition = "[System.Id] = {0}";
        private const string IdNotCondition = "[System.Id] <> {0}";
        private const string RevCondition = "[System.Rev] > {0}";
        private const string BoundaryStart = " ( ";
        private const string BoundaryEnd = " ) ";

        private bool _hasCondition = false;
        private readonly StringBuilder _wiqlQuery = new StringBuilder();

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the length of the builded query.
        /// </summary>
        public int Length
        {
            get { return _wiqlQuery.Length; }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new WiqlQueryBuilder instance, no public constructor
        /// needed.
        /// </summary>
        /// <param name="input">Input string to get query from.</param>
        /// <param name="arguments">Argument which should be inserted into the input string.</param>
        public WiqlQueryBuilder(string input, params object[] arguments)
        {
            PreCondition.AssertNotNullOrEmpty(input, "input");

            _wiqlQuery.AppendFormat(input, arguments);
        }

        /// <summary>
        /// Creates a new WiqlQueryBuilder instance, no public constructor
        /// needed.
        /// </summary>
        public WiqlQueryBuilder()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Appends the expression left with an or condition to the expression
        /// on the right side.
        /// </summary>
        /// <param name="left">Specifies the left expression.</param>
        /// <param name="right">Specifies the right expression.</param>
        public static WiqlQueryBuilder CombineWithOrCondition(WiqlQueryBuilder left, WiqlQueryBuilder right)
        {
            PreCondition.AssertNotNull(left, "left");
            PreCondition.AssertNotNull(right, "right");

            WiqlQueryBuilder builder = new WiqlQueryBuilder();

            if (left.Length > 0 && right.Length > 0)
            {
                builder._wiqlQuery.Append(BoundaryStart);
                builder._wiqlQuery.Append(left._wiqlQuery.ToString());
                builder._wiqlQuery.Append(BoundaryEnd);
                builder._wiqlQuery.Append(ConditionOrLink);
                builder._wiqlQuery.Append(BoundaryStart);
                builder._wiqlQuery.Append(right._wiqlQuery.ToString());
                builder._wiqlQuery.Append(BoundaryEnd);
            }
            return builder;
        }

        /// <summary>
        /// Appends the expression left with an or condition to the expression
        /// on the right side.
        /// </summary>
        /// <param name="left">Specifies the left expression.</param>
        /// <param name="right">Specifies the right expression.</param>
        public static WiqlQueryBuilder CombineWithAndCondition(WiqlQueryBuilder left, WiqlQueryBuilder right)
        {
            PreCondition.AssertNotNull(left, "left");
            PreCondition.AssertNotNull(right, "right");

            WiqlQueryBuilder builder = new WiqlQueryBuilder();

            if (left.Length > 0 && right.Length > 0)
            {
                builder._wiqlQuery.Append(BoundaryStart);
                builder._wiqlQuery.Append(left._wiqlQuery.ToString());
                builder._wiqlQuery.Append(BoundaryEnd);
                builder._wiqlQuery.Append(ConditionAndLink);
                builder._wiqlQuery.Append(BoundaryStart);
                builder._wiqlQuery.Append(right._wiqlQuery.ToString());
                builder._wiqlQuery.Append(BoundaryEnd);
            }
            return builder;
        }

        /// <summary>
        /// Appends the given id and an or condition, if another condition
        /// already exists.
        /// </summary>
        /// <param name="id">Id of the element to check.</param>
        public void AppendIdOrCondition(int id)
        {
            if (!_hasCondition)
                _hasCondition = true;
            else
                _wiqlQuery.AppendLine(ConditionOrLink);

            _wiqlQuery.AppendFormat(IdCondition, id);
        }

        /// <summary>
        /// Appends the given id (negation) and an or condition, if another
        /// condition already exists.
        /// </summary>
        /// <param name="id">Id of the element to check.</param>
        public void AppendIdOrNotCondition(int id)
        {
            if (!_hasCondition)
                _hasCondition = true;
            else
                _wiqlQuery.AppendLine(ConditionOrLink);

            _wiqlQuery.AppendFormat(IdNotCondition, id);
        }

        /// <summary>
        /// Appends the given id/rev and an or condition, if another condition
        /// already exists.
        /// </summary>
        /// <param name="id">Id of the element to check.</param>
        /// <param name="revision">Specifies the element revision.</param>
        public void AppendIdAndRevisionOrCondition(int id, int revision)
        {
            if (!_hasCondition)
                _hasCondition = true;
            else
                _wiqlQuery.AppendLine(ConditionOrLink);

            _wiqlQuery.Append(BoundaryStart);
            _wiqlQuery.AppendFormat(IdCondition, id);
            _wiqlQuery.Append(ConditionAndLink);
            _wiqlQuery.AppendFormat(RevCondition, revision);
            _wiqlQuery.Append(BoundaryEnd);
        }

        /// <summary>
        /// Appends the given ids and an or condition, if another condition
        /// already exists.
        /// </summary>
        /// <param name="id">Id of the element to check.</param>
        public void AppendIdOrConditions(IEnumerable<int> id)
        {
            PreCondition.AssertNotNull(id, "id");

            id.ForEach(AppendIdOrCondition);
        }

        /// <summary>
        /// Appends the given ids and an or condition, if another condition
        /// already exists.
        /// </summary>
        /// <param name="conditions">Conditions to append.</param>
        /// <param name="conditionFormat">Specifies the condition format.</param>
        public void AppendOrConditions<T>(IEnumerable<T> conditions, string conditionFormat = null)
        {
            PreCondition.AssertNotNull(conditions, "conditions");

            foreach (var condition in conditions)
            {
                if (!_hasCondition)
                    _hasCondition = true;
                else
                    _wiqlQuery.AppendLine(ConditionOrLink);

                if (!string.IsNullOrEmpty(conditionFormat))
                {
                    _wiqlQuery.AppendFormat(conditionFormat, condition);
                }
                else
                {
                    _wiqlQuery.Append(condition);
                }
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return _wiqlQuery.ToString();
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}